Skip to content

fix(query-core): clear timers when timer ID is 0#10401

Open
semimikoh wants to merge 2 commits intoTanStack:mainfrom
semimikoh:fix/timer-id-zero-falsy-check
Open

fix(query-core): clear timers when timer ID is 0#10401
semimikoh wants to merge 2 commits intoTanStack:mainfrom
semimikoh:fix/timer-id-zero-falsy-check

Conversation

@semimikoh
Copy link
Copy Markdown

@semimikoh semimikoh commented Apr 6, 2026

Custom TimeoutProvider implementations may return 0 as a valid timer
ID (e.g. a counter-based provider), but the existing truthy checks
treated 0 as "no timer" and skipped clearTimeout/clearInterval. This
left stale timers running, causing unexpected refetches and GC leaks.

Compare against undefined instead, matching the optional ?: ManagedTimerId field types.

Fixes #10395

🎯 Changes

Replace truthy checks on ManagedTimerId fields with explicit !== undefined
comparisons so that a timer ID of 0 (legal for custom TimeoutProviders) is still
cleared:

  • packages/query-core/src/removable.tsclearGcTimeout (#gcTimeout)
  • packages/query-core/src/queryObserver.ts#clearStaleTimeout
    (#staleTimeoutId)
  • packages/query-core/src/queryObserver.ts#clearRefetchInterval
    (#refetchIntervalId)

The fields are typed ?: ManagedTimerId, so undefined is the correct sentinel for
"no timer".

✅ Checklist

  • I have followed the steps in the Contributing
    guide
    .
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a [changeset](https://g
    ithub.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

Bug Fixes

  • Fixed timer ID clearing logic to properly handle custom TimeoutProviders that return 0 as a valid timer ID. Timers are now correctly cleared when using custom timeout providers that may return falsy timer IDs.

  Custom TimeoutProvider implementations may return 0 as a valid timer
  ID (e.g. a counter-based provider), but the existing truthy checks
  treated 0 as "no timer" and skipped clearTimeout/clearInterval. This
  left stale timers running, causing unexpected refetches and GC leaks.

  Compare against undefined instead, matching the optional `?:
  ManagedTimerId` field types.

  Fixes TanStack#10395
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a37bebd9-063b-40cf-ba5b-9f7c89eda1db

📥 Commits

Reviewing files that changed from the base of the PR and between eac62b8 and cb75475.

📒 Files selected for processing (3)
  • .changeset/fix-timer-id-zero-falsy-check.md
  • packages/query-core/src/queryObserver.ts
  • packages/query-core/src/removable.ts

📝 Walkthrough

Walkthrough

This PR fixes timer cleanup logic in TanStack Query Core by replacing falsy truthiness checks with explicit undefined comparisons. This ensures timer IDs with value 0 from custom timeout providers are properly cleared, preventing stale timers from running.

Changes

Cohort / File(s) Summary
Timer Cleanup Fixes
packages/query-core/src/queryObserver.ts, packages/query-core/src/removable.ts
Updated #clearStaleTimeout(), #clearRefetchInterval(), and clearGcTimeout() methods to use explicit !== undefined checks instead of falsy truthiness checks, allowing timer ID 0 to be correctly cleared.
Changeset Entry
.changeset/fix-timer-id-zero-falsy-check.md
Added patch release entry documenting the fix for timer ID 0 handling in custom TimeoutProvider implementations.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 Timers that tick, even when zero,
Now hop past the falsy snare,
With checks explicit, clear and fair,
No stale ticks left in the lair,
Our cleanup dance makes hearts light! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: ensuring timers with ID 0 are properly cleared instead of being treated as falsy/absent.
Description check ✅ Passed The description thoroughly explains the problem, the solution, includes all required sections and checklist items, and identifies affected files with specific implementation details.
Linked Issues check ✅ Passed The PR directly addresses all requirements from issue #10395: replaces truthy checks with explicit !== undefined comparisons for timer ID fields (#gcTimeout, #staleTimeoutId, #refetchIntervalId).
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the timer ID 0 handling issue: three files modified with focused changes to timer cleanup logic and one changeset entry added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Falsy timer ID (0) prevents proper timeout/interval cleanup with custom timeout provider

1 participant